﻿namespace WLFramework.Web.Binder.ValueProvider
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Globalization;
    using System.Linq;
    using System.Text;
    using WLFramework.Common;
    using WLFramework.Reflection;

    public class ValueProviderResult
    {
        private bool isConverted = false;
        private object targetValue = null;

        public object RawValue { get; set; }

        public ValueProviderResult()
        {
        }

        public ValueProviderResult(object value)
        {
            Array valueAsArray = value as Array;
            if (valueAsArray != null && valueAsArray.Length == 1)
            {
                this.RawValue = valueAsArray.GetValue(0);
            }
            else
            {
                this.RawValue = value;
            }
        }

        public object ConvertTo(Type destinationType)
        {
            if (this.isConverted)
            {
                return this.targetValue;
            }

            this.targetValue = UnwrapPossibleArrayType(this.RawValue, destinationType);
            this.isConverted = true;
            return this.targetValue;
        }

        public bool IsNull()
        {
#if FW4
            Array valueAsArray = this.RawValue as Array;
            if (valueAsArray != null)
            {
                foreach (var value in valueAsArray)
                {
                    if (value != null ||
                       !string.IsNullOrWhiteSpace(value.ToString()))
                    {
                        return false;
                    }
                }

                return true;
            }

            return RawValue == null ||
                   string.IsNullOrWhiteSpace(RawValue.ToString());
#else
            Array valueAsArray = this.RawValue as Array;
            if (valueAsArray != null)
            {
                foreach (var value in valueAsArray)
                {
                    if (value != null ||
                        value.ToString().Trim() == string.Empty)
                    {
                        return false;
                    }
                }

                return true;
            }

            return RawValue == null ||
                   RawValue.ToString().Trim() == string.Empty;
#endif
        }

        public bool AnyOneIsNull()
        {
#if FW4
            Array valueAsArray = this.RawValue as Array;
            if (valueAsArray != null)
            {
                foreach (var value in valueAsArray)
                {
                    if (value == null ||
                       string.IsNullOrWhiteSpace(value.ToString()))
                    {
                        return true;
                    }
                }

                return false;
            }

            return RawValue == null ||
                   string.IsNullOrWhiteSpace(RawValue.ToString());
#else
            Array valueAsArray = this.RawValue as Array;
            if (valueAsArray != null)
            {
                foreach (var value in valueAsArray)
                {
                    if (value == null ||
                        value.ToString().Trim() == string.Empty)
                    {
                        return true;
                    }
                }

                return false;
            }

            return RawValue == null ||
                   RawValue.ToString().Trim() == string.Empty;
#endif
        }

        private static object ConvertSimpleType(object value, Type destinationType)
        {
            if (value == null || destinationType.IsInstanceOfType(value))
            {
                return value;
            }

            string valueAsString = value as string;

#if FW4
            if (string.IsNullOrWhiteSpace(valueAsString))
            {
                return null;
            }
#else
            if (valueAsString == null ||
                valueAsString.Trim() == string.Empty)
            {
                return null;
            }
#endif

            TypeConverter converter = TypeDescriptor.GetConverter(destinationType);
            bool canConvertFrom = converter.CanConvertFrom(value.GetType());
            if (!canConvertFrom)
            {
                converter = TypeDescriptor.GetConverter(value.GetType());
            }

            if (!(canConvertFrom || converter.CanConvertTo(destinationType)))
            {
                throw new Exception(
                    string.Format("无法将{0}转换成{1}", value.GetType().FullName, destinationType.FullName));
            }

            object convertedValue = canConvertFrom ?
                    converter.ConvertFrom(null, CultureInfo.InvariantCulture, value) :
                    converter.ConvertTo(null, CultureInfo.InvariantCulture, value, destinationType);
            return convertedValue;
        }

        private static object UnwrapPossibleArrayType(object value, Type destinationType)
        {
            if (value == null || destinationType.IsInstanceOfType(value))
            {
                return value;
            }

            // array conversion results in four cases, as below
            Array valueAsArray = value as Array;
            if (destinationType.IsArray)
            {
                Type destinationElementType = destinationType.GetElementType();
                if (valueAsArray != null)
                {
                    // case 1: both destination + source type are arrays, so convert each element
                    IList converted = Array.CreateInstance(destinationElementType, valueAsArray.Length);
                    for (int i = 0; i < valueAsArray.Length; i++)
                    {
                        converted[i] = ConvertSimpleType(valueAsArray.GetValue(i), destinationElementType);
                    }

                    return converted;
                }
                else
                {
                    // case 2: destination type is array but source is single element, so wrap element in array + convert
                    object element = ConvertSimpleType(value, destinationElementType);
                    IList converted = Array.CreateInstance(destinationElementType, 1);
                    converted[0] = element;
                    return converted;
                }
            }
            else if (valueAsArray != null)
            {
                if (valueAsArray.Length > 0)
                {
                    // case 3: destination type is single element but source is array, so extract first element + convert
                    value = valueAsArray.GetValue(0);
                    return ConvertSimpleType(value, destinationType);
                }
                else
                {
                    // case 3(a): source is empty array, so can't perform conversion
                    return null;
                }
            }

            // case 4: both destination + source type are single elements, so convert
            return ConvertSimpleType(value, destinationType);
        }
    }
}
